/*
 * Copyright (c) 2015-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 */

package com.facebook.common.executors;

import java.util.concurrent.atomic.AtomicInteger;

/**
 * Abstraction for computation.
 * <p/>
 * <p> Computation expressed as StatefulRunnable can be cancelled, but only if it has not
 * started yet.
 * <p/>
 * <p> For better decoupling of the code computing the result and the code that handles it, 4
 * separate methods are provided: getResult, onSuccess, onFailure and onCancellation.
 * <p/>
 * <p> This runnable can be run only once. Subsequent calls to run method won't have any effect.
 */
abstract public class StatefulRunnable<T> implements Runnable {
    protected static final int STATE_CREATED = 0;
    protected static final int STATE_STARTED = 1;
    protected static final int STATE_CANCELLED = 2;
    protected static final int STATE_FINISHED = 3;
    protected static final int STATE_FAILED = 4;

    protected final AtomicInteger mState;

    public StatefulRunnable() {
        mState = new AtomicInteger(STATE_CREATED);
    }

    @Override
    public final void run() {
        if (!mState.compareAndSet(STATE_CREATED, STATE_STARTED)) {
            return;
        }
        T result;
        try {
            result = getResult();
        } catch (Exception e) {
            mState.set(STATE_FAILED);
            onFailure(e);
            return;
        }

        mState.set(STATE_FINISHED);
        try {
            onSuccess(result);
        } finally {
            disposeResult(result);
        }
    }

    public void cancel() {
        if (mState.compareAndSet(STATE_CREATED, STATE_CANCELLED)) {
            onCancellation();
        }
    }

    /**
     * Called after computing result successfully.
     *
     * @param result
     */
    protected void onSuccess(T result) {
    }

    /**
     * Called if exception occurred during computation.
     *
     * @param e
     */
    protected void onFailure(Exception e) {
    }

    /**
     * Called when the runnable is cancelled.
     */
    protected void onCancellation() {
    }

    /**
     * Called after onSuccess callback completes in order to dispose the result.
     *
     * @param result
     */
    protected void disposeResult(T result) {
    }

    abstract protected T getResult() throws Exception;
}
